import Watcher from "./watcher.js"

export default class Compiler {
    constructor(vm) {
        this.el = vm.$el
        this.vm = vm
        this.methods = vm.$options.methods
        this.compile(this.el)
    }
    // 编译模板，处理文本节点和元素节点
    compile (el) {
        [...el.childNodes].forEach(node => {
            if (this.isTextNode(node)) {
                this.compileText(node)
            }
            if (this.isElementNode(node)) {
                this.compileElement(node)
            }
            // 判断childNodes并递归调用compile
            if (node.childNodes && node.childNodes.length) {
                this.compile(node)
            }
        })
    }

    // 编译元素节点，处理指令
    compileElement (node) {
        // 获取并遍历元素的属性节点
        [...node.attributes].forEach(attr => {
            const { name, value } = attr
            if (this.isDirective(name)) {
                this.update(node, name.slice(2), value)
            }
        })
    }

    // 处理指令
    update (node, name, value) {
        const [directiveName, directiveArgs] = this.getDirectiveArgs(name)
        console.log(name, directiveName, directiveArgs)

        const fn = this[`${directiveName}Updater`]
        typeof fn === 'function' && fn.call(this, node, value, directiveArgs)
    }

    // 处理v-text指令
    textUpdater(node, value) {
        node.textContent = this.vm[value]
        // 创建watcher对象，当数据改变时更新视图
        new Watcher(this.vm, value, newValue => {
            node.textContent = newValue
        })
    }

    // 处理v-model指令
    modelUpdater(node, value) {
        node.value = this.vm[value]
        // 创建watcher对象，当数据改变时更新视图
        new Watcher(this.vm, value, newValue => {
            node.value = newValue
        })
        // 注册表单input事件实现双向绑定
        node.addEventListener('input', () => {
            this.vm[value] = node.value
        })
    }    


    // 处理v-on指令    
    // 只处理了指令值value是methodName的方式，未处理内联语句及对象方式
    // 简单处理了指令修饰符
    onUpdater(node, value, args) {
        if (!args) {
            return
        }

        const _this = this
        const fn = _this.methods[value]
        const [eventName, eventModifier] = this.getModifier(args)

        node.addEventListener(eventName, function(e) {
            // method调用this指向vm实例
            typeof fn === 'function' && fn.call(this.vm)
            
            // 处理指令修饰符
            eventModifier === 'stop' && e.stopPropagation()
            eventModifier === 'prevent' && e.preventDefault()
        }, {
            capture: eventModifier === 'capture',
            once: eventModifier === 'once',
            passive: eventModifier === 'passive',
        })
        
    }

    // 处理v-html指令
    // 更新元素的innerHTML
    htmlUpdater(node, value) {
        node.innerHTML = this.vm[value]
    }

    // 编译文本节点，处理差值表达式
    compileText (node) {
        // 使用正则表达式匹配差值表达式 {{ xxx }}，并提取获取表达式内容
        let reg = /\{\{(.+?)\}\}/
        const text = node.textContent
        node.textContent = text.replace(reg, (word, key) => {
            key = key.trim()
            if (key in this.vm) {
                // 创建watcher对象，当数据改变时更新视图
                new Watcher(this.vm, key, newValue => {                    
                    node.textContent = text.replace(reg, (w, k) => this.vm[k.trim()])
                })
                return this.vm[key]
            }
            return word            
        })        
    }

    // 判断属性是否为指令
    isDirective (attrName) {
        return attrName.startsWith('v-')
    }

    // 判断是否为元素节点
    isElementNode (node) {
        return node.nodeType === 1
    }
    // 判断是否为文本节点
    isTextNode (node) {
        return node.nodeType === 3
    }

    // 处理带参数的指令 v-on v-bind 等
    // 格式是 指令名称 + 冒号 + 指令参数，例如 v-on: click=
    getDirectiveArgs (name) {
        const [directiveName, args] = name.split(':')
        return [directiveName, args]
    }

    // 处理指令修饰符
    // 格式是 事件 + 点号 + 修饰符，例如 click.stop=
    getModifier (event) {
        const [eventName, modifier] = event.split('.')
        return [eventName, modifier]
    }
}